
/****
 * Copyright (C) 2006 dNux Team
 *                                                            ****
 * This program is free software; you can redistribute it and/or *
 * modify under the terms of the GNU General Public License as   *
 * published by the Free Software Foundation;                    *
 * either version 2 of the License.                              *
 *                                                               *
 * This program is distributed in the hope that it will be       *
 * useful, but WITHOUT ANY WARRANTY; without even the implied    *
 * warranty of without even the implied waranty of               *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.          *
 * See the GNU General Public License for more details.          *
 *                                                               *
 * You should have received a copy of the                        *
 * GNU General Public License                                    *
 * along with this program; if not, write to the                 *
 * Free Software Foundation, Inc.,                               *
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. *
 *                                                               *
 ****                                                         ****/

/**
 * @file list.c
 * @brief functions for the manipulation of list type.
 * @addtogroup list
 * @{
 * 	@brief Lists functions.
 */

#include "dcore.h"

/**
 * @typedef d_list_t
 * @brief list type.
 */

/**
 * @typedef d_list_iterator_t
 * @brief list iterator type.
 */

/**
 * @typedef d_list_free_f
 * @brief free func declaration
 */

/**
 * @brief Allocate a new empty list
 * @return the new list or NULL.
 */
d_list_t *
d_list_new ( void )
{
  d_list_t *p_new = NULL;
  
  p_new = (d_list_t *) malloc(sizeof(d_list_t));
  
  if ( p_new != NULL ) {
    memset(p_new, 0, sizeof(d_list_t));
  }
  
  return p_new;
}

d_list_t *
d_list_new_with_free_func( d_list_free_f f_func )
{
  d_list_t *new;
  new = d_list_new();
  d_list_set_free_func(new, f_func);
  return new;
}

/**
 * @brief Free memory of a list.
 * @param p_list a list.
 */
void
d_list_free ( d_list_t** p_list )
{
	if (*p_list == NULL ) return;

	d_list_clear(*p_list);

	free(*p_list);
	
	*p_list = NULL;
}

/**
 * @brief Set a func called for the data when d_list_free is called.
 * @param p_list a list
 * @param f_free the func.
 */
void
d_list_set_free_func ( d_list_t *p_list, d_list_free_f f_free)
{
	if (p_list == NULL) return;

	p_list->f_free = f_free;
}

/**
 * @brief Free memory of an iterator
 * @param p_iterator pointer to iterator
 */
void
d_list_free_iterator ( d_list_iterator_t **p_iterator )
{
	if (*p_iterator != NULL ) {
		free(*p_iterator);
	}
	*p_iterator = NULL;
}

/**
 * @internal
 * @brief Create a new element
 * @param p_data the data
 * @return the new ellement, or NULL
 */
_d_list_element_t *
_d_list_new_element(void* p_data)
{
	_d_list_element_t *p_new = NULL;

	/* check data */
	if (p_data == NULL) {
		return p_new;
	}

	/* allocate the new element */
	p_new = (_d_list_element_t *) malloc(sizeof(_d_list_element_t));
	if (p_new == NULL) {
		return p_new;
	}

	/* Fill data */
	p_new->p_data = p_data;
	p_new->p_prev = NULL;
	p_new->p_next = NULL;

	return p_new;
}

/**
 * @internal
 * @brief create a new iterator.
 * @param p_list a list
 * @param p_element an element on the list
 * @return a new iterator or NULL
 */

d_list_iterator_t*
_d_list_new_iterator (d_list_t *p_list, _d_list_element_t *p_element)
{
	d_list_iterator_t *p_new = NULL;

	if (p_list == NULL) {
		return p_new;
	}

	if (p_element == NULL) {
		return p_new;
	}

	p_new = (d_list_iterator_t*) malloc(sizeof(d_list_iterator_t));
	if (p_new == NULL) {
		return p_new;
	}

	p_new->p_list = p_list;
	p_new->p_current = p_element;

	return p_new;
}

/**
 * @def d_list_push(p_list, p_data)
 * @brief Add an element at the begin of a list.
 * @param p_list a list.
 * @param p_data data pointer.
 * @return d_true on success, d_false on error.
 */

/**
 * @def d_list_pushback(p_list, p_data)
 * @brief Add an element at the end of a list.
 * @param p_list a list.
 * @param p_data data pointer.
 * @return d_true on success, d_false on error.
 */

/**
 * @internal
 * @brief Add a new element to the list.
 * @param p_list the list.
 * @param p_data data pointer.
 * @param b_at_end choise the position of the new element
 * @return d_true on success, d_false on error.
 *
 * You should use d_list_add macro.
 */
d_bool_t
_d_list_add (d_list_t *p_list, void *p_data, d_bool_t b_at_end)
{
	_d_list_element_t* p_new = _d_list_new_element(p_data);
	
	/* check the new ellement */
	if (p_new == NULL) {
		return d_false;
	}

	/* insert at the end. */
	if (b_at_end) {
		p_new->p_prev = p_list->p_end;
		if (p_new->p_prev != NULL) {
			p_new->p_prev->p_next = p_new;
		}
		p_list->p_end = p_new;
	}
	/* insert at the begin */
	else {
		p_new->p_next = p_list->p_begin;
		if (p_new->p_next != NULL) {
			p_new->p_next->p_prev = p_new;
		}
		p_list->p_begin = p_new;
	}

	/* if it's the first element of the list then fill p_begin & p_end */
	if (p_list->p_begin == NULL || p_list->p_end == NULL) {
		p_list->p_begin = p_new;
		p_list->p_end = p_new;
	}

	/* incremente length */
	p_list->i_length++;

	return d_true;
}

void
d_list_del (d_list_iterator_t *p_iterator)
{
  _d_list_element_t *p_next = NULL, *p_prev = NULL;

  if (p_iterator->p_list->f_free != NULL)
    p_iterator->p_list->f_free(p_iterator->p_current->p_data);
  
  p_next = p_iterator->p_current->p_next;
  p_prev = p_iterator->p_current->p_prev;

  if (! p_next && p_prev ) {
    p_iterator->p_list->p_end = p_prev;
    p_iterator->p_current = p_prev;
    p_prev->p_next = NULL;
  }
  if (! p_prev && p_next ) {
    p_iterator->p_list->p_begin = p_next;
    p_iterator->p_current = p_next;
    p_next->p_prev = NULL;
  }
  if (! p_prev && !p_next ) {
    p_iterator->p_list->p_begin = NULL;
    p_iterator->p_list->p_end = NULL;
    p_iterator->p_current = NULL;
  }
  if ( p_next && p_prev ) {
    p_next->p_prev = p_prev;
    p_prev->p_next = p_next;
    p_iterator->p_current = p_prev;
  }
  p_iterator->p_list->i_length--;
}

/**
 * @brief clear the list
 * @param p_list a list.
 */
void
d_list_clear (d_list_t *p_list)
{
	d_list_iterator_t *p_iterator = NULL;

	if (p_list == NULL ) return;
	
	p_iterator = d_list_begin(p_list);
	while (d_list_iterator_valid(p_iterator)) {
	  d_list_del(p_iterator);
	}

	p_list->i_length = 0;
}

/**
 * @brief retrieve list length
 * @param p_list a list
 * @return list length or -1 if list is NULL
 */
int
d_list_length(d_list_t *p_list)
{
	if (p_list == NULL) return -1;
	return p_list->i_length;
}

/**
 * @brief check if an iterator is valid
 * @param p_iterator an iterator.
 * @return d_false or d_true
 */
d_bool_t
d_list_iterator_valid (d_list_iterator_t *p_iterator)
{
	if (p_iterator == NULL) return d_false;
	if (p_iterator->p_list == NULL ) return d_false;
	if (p_iterator->p_current == NULL ) return d_false;
	return d_true;
}

/**
 * @brief get an itererator at a position.
 * @param p_list a list.
 * @param i_pos a pos.
 * @return the iterator or NULL
 */
d_list_iterator_t*
d_list_at(d_list_t *p_list, unsigned int i_pos)
{
	d_list_iterator_t *p_iterator;
	unsigned int i = 0;

	for (p_iterator = d_list_begin(p_list);
	     d_list_iterator_valid(p_iterator);
	     d_list_next(&p_iterator) ) {

		if (i_pos == i) 
			return 	p_iterator;
		i++;
	}
	d_list_free_iterator(&p_iterator);
	return NULL;
}

/**
 * @def d_list_value (p_element, p_data)
 * @param p_iterator an iterator
 * @param p_data pointer where data wille be store.
 * @return d_true or d_false
 */

/**
 * @internal
 * @brief retrieve a value
 * @param p_iterator an iterator
 * @param poiter where data will be store.
 * @return d_true or d_false
 */
d_bool_t
_d_list_value (d_list_iterator_t *p_iterator, void** p_data)
{
	*p_data = NULL;

	if (p_iterator == NULL) return d_false;
	if (p_iterator->p_current == NULL) return d_false;
	if (p_iterator->p_current->p_data == NULL) return d_false;

	*p_data = p_iterator->p_current->p_data;
	return d_true;
}

/**
 * @brief retrieve an iterator on the begin of a list
 * @param p_list a list.
 * @return an iterator or NULL.
 */
d_list_iterator_t *
d_list_begin (d_list_t *p_list)
{
	if (p_list == NULL) return NULL;
	if (p_list->p_begin == NULL) return NULL;

	return _d_list_new_iterator(p_list, p_list->p_begin);
}

/**
 * @brief retrieve an iterator on the end of a list
 * @param p_list a list.
 * @return an iterator or NULL.
 */
d_list_iterator_t *
d_list_end (d_list_t *p_list)
{
	if (p_list == NULL) return NULL;
	if (p_list->p_end == NULL) return NULL;

	return _d_list_new_iterator(p_list, p_list->p_end);
}

/**
 * @brief iterator--
 * @param p_iterator pointer to a valid iterator.
 * @return d_true or d_false
 */
d_bool_t
d_list_prev (d_list_iterator_t **p_iterator)
{
	if (p_iterator == NULL) return d_false;
	
	(*p_iterator)->p_current = (*p_iterator)->p_current->p_prev;

	if ((*p_iterator)->p_current == NULL) return d_false;
	return d_true;
}

/**
 * @brief iterator++
 * @param p_iterator pointer to a valid iterator.
 * @return d_true or d_false
 */
d_bool_t
d_list_next (d_list_iterator_t **p_iterator)
{
	if (*p_iterator == NULL) return d_false;

	(*p_iterator)->p_current = (*p_iterator)->p_current->p_next;

	if ((*p_iterator)->p_current == NULL) return d_false;
	return d_true;
}

/**
 * @}
 */

